'''
微信支付
第三方sdk ：wechatpayv3
源码地址：https://gitee.com/minibear2021/wechatpayv3.git
'''

import logging
import os
import json
import datetime
from decimal import Decimal

from django.utils import timezone
from django.conf import settings
from django.db.models import F
from django.core.cache import cache

from wechatpayv3 import WeChatPay, WeChatPayType

from ....models.chat_orders import ChatOrders
from ....models.users import Users
from ....models.chat_product import ChatProduct
from ....models.powerlogs import PowerLogs

from aiapi.utils.data_handle import users_data_paging


# 初始化微信支付的一些配置

# 微信支付商户ID（直连模式）或服务商商户号（服务商模式，即sp_mchid)
MCHID = settings.WECHAT_PAY_CONFIG['MchId']

# 商户证书私钥
with open(settings.WECHAT_PAY_CONFIG['PrivateKey'], 'r') as f:
    PRIVATE_KEY = f.read()

# 商户证书序列号
CERT_SERIAL_NO = settings.WECHAT_PAY_CONFIG['SerialNo']

# API v3密钥， https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_2.shtml
APIV3_KEY = settings.WECHAT_PAY_CONFIG['ApiV3Key']

# APPID，应用ID或服务商模式下的sp_appid
APPID = settings.WECHAT_PAY_CONFIG['AppId']

# 回调地址，也可以在调用接口的时候覆盖
NOTIFY_URL = settings.WECHAT_PAY_CONFIG['NotifyURL']

# 微信支付平台证书缓存目录，减少证书下载调用次数，首次使用确保此目录为空目录.
# 初始调试时可不设置，调试通过后再设置，示例值:'./cert'
CERT_DIR = "./cert"

################################################################
# 获取当前时间
now = datetime.datetime.now()
# 格式化为 YYYY-MM-DD
formatted_date = now.strftime('%Y-%m-%d')

logsfile = 'wechatpay-{}.log'.format(formatted_date)

# 日志记录器，记录web请求和回调细节
logging.basicConfig(filename=os.path.join(os.getcwd(), 'logs/', logsfile), level=logging.INFO, filemode='a', format='%(asctime)s - %(process)s - %(levelname)s: %(message)s')

LOGGER = logging.getLogger("wechatpay")
#################################################################

# 接入模式:False=直连商户模式，True=服务商模式
PARTNER_MODE = False

# 代理设置，None或者{"https": "http://10.10.1.10:1080"}，详细格式参见https://requests.readthedocs.io/en/latest/user/advanced/#proxies
PROXY = None

# 请求超时时间配置
TIMEOUT = (10, 30) # 建立连接最大超时时间是10s，读取响应的最大超时时间是30s


########################################################
# 初始化实例
# 初始化
wxpay = WeChatPay(
    wechatpay_type=WeChatPayType.NATIVE,
    mchid=MCHID,
    private_key=PRIVATE_KEY,
    cert_serial_no=CERT_SERIAL_NO,
    apiv3_key=APIV3_KEY,
    appid=APPID,
    notify_url=NOTIFY_URL,
    cert_dir=CERT_DIR,
    logger=LOGGER,
    partner_mode=PARTNER_MODE,
    proxy=PROXY,
    timeout=TIMEOUT
)


# 统一下单
def wechatpay(totalfee, orderno, subject):
    # 以native下单为例，下单成功后即可获取到'code_url'，将'code_url'转换为二维码，并用微信扫码即可进行支付测试。
    '''
    totalfee: 总价格
    orderno: 自定义订单编号
    subject: 商品描述 订单名称

    res = {
        "code": 200,
        "message": "{\"code_url\":\"weixin://wxpay/bizpayurl?pr=abcdefghi\"}"
    }

    res :
    {'code': 400, 'message': '{"code":"PARAM_ERROR","detail":{"location":"body","value":"0.01"},"message":"无法将 JSON 输入源“/body/amount/total”映射到目标字段“总金额”中，此字段需要一个合法的 64 位有符号整数"}'}
    {'code': 400, 'message': '{"code":"PARAM_ERROR","detail":{"location":"body","value":0},"message":"输入源“/body/amount/total”映射到数值字段“总金额”规则校验失败，值低于最小值 1"}'}
    {'code': 403, 'message': '{"code":"NO_AUTH","message":"此商家的收款功能已被限制，暂无法支付。商家可登录微信商户平台/微信支付商家助手小程序/经营账户页面查看原因和解决方案。"}'}
    '''

    code, message = wxpay.pay(
        description=subject,
        out_trade_no=orderno,
        amount={'total': int(totalfee * Decimal('100'))},
        pay_type=WeChatPayType.NATIVE
    )

    if code == 200:
        dict_code_url = json.loads(message)
        code_url_str = dict_code_url['code_url']
        res_mes = {
            'code': 200,
            'message': code_url_str
        }
    else:
        res_mes = {
            'code': 400,
            'message': message
        }


    return res_mes


def chaeck_wxpay_trade(trade_no):
    '''
    校验订单支付状态
    去查询订单的 状态
    交易状态】 交易状态，枚举值：
    * SUCCESS：支付成功
    * REFUND：转入退款
    * NOTPAY：未支付
    * CLOSED：已关闭
    * REVOKED：已撤销（仅付款码支付会返回）
    * USERPAYING：用户支付中（仅付款码支付会返回）
    * PAYERROR：支付失败（仅付款码支付会返回）
    '''
    mes = ""

    code, message = wxpay.query(
        out_trade_no = str(trade_no)
    )

    message_dict = json.loads(message)
    
    if code == 200:
        if message_dict["trade_state"] == "SUCCESS":
            return mes
        else:
            mes = "error"
            return mes
    else:
        mes = "error"
        return mes


def payment_success(trade_no):
    '''
    异步通知回调公共逻辑
    更新订单状态
    给用户新增算力
    更新算力日志
    '''
    mes = ""

    chat_order_obj = ChatOrders.objects.filter(order_no=trade_no)
    if chat_order_obj:
        # 判断订单状态是否已完成 如果订单状态是已完成 就直接返回
        if chat_order_obj[0].status == 2:
            return mes
        
        # 判断订单用户id 是否在用户表存在
        order_userid = chat_order_obj[0].user_id
        user_obj = Users.objects.filter(id=order_userid)
        if not user_obj:
            mes = "获取用户信息时出错！"
            return mes
        
        # 判断订单是否有备注信息
        order_remark = chat_order_obj[0].remark
        if type(order_remark) != dict:
            mes = "解析订单备注信息失败！"
            return mes

        # 如果付款成功了
        # 给用户增加算力或者续费vip
        opt = ""
        recharge_power = 0

        try:
            u_obj = user_obj[0]
            if order_remark['days'] > 0:
                # 如果充值天数大于0 就是 续费vip 并且增加算力
                # 如果用户第一次开通vip，那就是开通vip，并且初始化算力

                # datetime.datetime.now().replace(tzinfo=datetime.timezone.utc) 这个后面的先去掉了，发现时间不对

                if u_obj.expired_time is None:
                    # 要是None的话 就是永久不到期用户, 那就是给他设定到期时间，并且增加算力
                    # u_obj.expired_time = datetime.datetime.now().replace(tzinfo=datetime.timezone.utc) + datetime.timedelta(days=order_remark['days'])
                    u_obj.expired_time = timezone.now() + datetime.timedelta(days=order_remark['days'])
                    u_obj.power = order_remark['power']
                    u_obj.vip = True
                    u_obj.save()
                # elif u_obj.expired_time.replace() >= datetime.datetime.now().replace(tzinfo=datetime.timezone.utc):
                elif u_obj.expired_time.replace() >= timezone.now():
                    user_obj.update(expired_time=F('expired_time') + datetime.timedelta(days=order_remark['days']), power=F('power') + order_remark['power'], vip=True)
                else:
                    # u_obj.expired_time = datetime.datetime.now().replace(tzinfo=datetime.timezone.utc) + datetime.timedelta(days=order_remark['days'])
                    u_obj.expired_time = timezone.now() + datetime.timedelta(days=order_remark['days'])
                    u_obj.power = order_remark['power']
                    u_obj.vip = True
                    u_obj.save()

                opt = "VIP充值"
                recharge_power = order_remark['power']

            else:
                # 如果充值产品为点卡充值，就是天数小于0 就是充值算力, 为用户充值算力
                # 月度会员算力有效期内有效，会员过期无效。 点卡充值的算力长期有效。vip充值点卡有效期内有效，非vip充值点卡长期有效！
                if u_obj.vip:
                    # vip
                    # user_obj.update(power=F('power') + order_remark['power'], expired_time=None, vip=False)
                    user_obj.update(power=order_remark['power'], expired_time=None, vip=False)
                else:
                    # 不是vip
                    user_obj.update(power=F('power') + order_remark['power'], expired_time=None, vip=False)

                opt = "点卡充值"
                recharge_power = order_remark['power']
        except BaseException as e:
            mes = "更新用户失败！"
            return mes

        # 更新订单状态
        try:
            chat_order_obj.update(status=2, trade_no=trade_no, pay_time=datetime.datetime.now())
        except:
            mes = "订单更新失败！"
            return mes
        
        # 更新产品销售量
        try:
            ChatProduct.objects.filter(id=chat_order_obj[0].product_id).update(sales=F('sales') + 1)
        except:
            pass

        # 记录算力充值日志
        # 先查询一次最新的用户信息
        zuixin_user_obj_list = Users.objects.filter(id=order_userid)
        zuixin_user_obj = zuixin_user_obj_list[0]
        if opt:
            remark_str = '{}, 金额: {}, 订单号: {}'.format(opt, chat_order_obj[0].amount, chat_order_obj[0].order_no)
            powerlogs_obj = PowerLogs(
                user_id = order_userid,
                username = zuixin_user_obj.username,
                type = 1,
                amount = recharge_power,
                balance = zuixin_user_obj.power,
                model = chat_order_obj[0].pay_way,
                remark = remark_str,
                mark = 1
            )

            try:
                powerlogs_obj.save()
            except:
                pass

        # 如果一切都完成了之后 就更新用户信息到redis缓存
        # 设置用户信息缓存到redis
        try:
            chatusers_tree_json = users_data_paging(zuixin_user_obj_list, 1, 10)
            cache.set(f'user_info_{order_userid}', chatusers_tree_json["items"][0], timeout=3600)  # 设置缓存超时时间为3600秒
        except:
            pass

        return mes

    else:
        mes = "没有这个订单"
        return mes


# 微信回调接口
def wxpay_notify(request):
    headers = {
        'Wechatpay-Signature': request.META.get('HTTP_WECHATPAY_SIGNATURE'),
        'Wechatpay-Timestamp': request.META.get('HTTP_WECHATPAY_TIMESTAMP'),
        'Wechatpay-Nonce': request.META.get('HTTP_WECHATPAY_NONCE'),
        'Wechatpay-Serial': request.META.get('HTTP_WECHATPAY_SERIAL')
    }
    result = wxpay.callback(headers=headers, body=request.body)

    if result and result.get('event_type') == 'TRANSACTION.SUCCESS':
        resp = result.get('resource')
        # appid = resp.get('appid')
        # mchid = resp.get('mchid')
        out_trade_no = resp.get('out_trade_no')
        # transaction_id = resp.get('transaction_id')
        # trade_type = resp.get('trade_type')
        # trade_state = resp.get('trade_state')
        # trade_state_desc = resp.get('trade_state_desc')
        # bank_type = resp.get('bank_type')
        # attach = resp.get('attach')
        # success_time = resp.get('success_time')
        # payer = resp.get('payer')
        # amount = resp.get('amount').get('total')
        # TODO: 根据返回参数进行必要的业务处理，处理完后返回200或204
        # 校验订单支付状态，为什么有回调里面的状态了 还需要查询一下订单状态。更严谨一点
        chaeck_trade_res = chaeck_wxpay_trade(out_trade_no)

        if chaeck_trade_res:
            return {'code': 'FAILED', 'message': '失败'}

        # 异步通知回调公共逻辑
        # 就是修改一些数据库的信息，比如订单表，日志表，充值表
        res = payment_success(out_trade_no)

        if res:
            return {'code': 'FAILED', 'message': '失败'}
        return {'code': 'SUCCESS', 'message': '成功'}
    else:
        return {'code': 'FAILED', 'message': '失败'}


